﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;

namespace WpfApp2
{
    partial class CircleBread
    {
        public CircleBread()
        {
            InitializeComponent();
            Radius = 50.0;
            Width = 120;
            Height = 120;
            StrokeTrickness = 8;
            RegisteInternalEvent();
        }
        public Point Center { get; private set; }
        private double _Radius;
        public double Radius 
        {
            get { return _Radius; }
            set
            {
                _Radius = value;
                this.MinWidth = _Radius * 2 + 20;
                this.MinHeight = _Radius * 2 + 20;
            }
        }
        public int StrokeTrickness { get; set; }
        public int StartAngle { get; set; } = 0;
        private Action<System.Windows.Shapes.Path> PathClickEvent;
        public void OnClickPath(Action<System.Windows.Shapes.Path> handler)
        {
            PathClickEvent = handler;
        }

        public IList<ArcElement> ItemSource
        {
            get { return (IList<ArcElement>)GetValue(ItemSourceProperty); }
            set { SetValue(ItemSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemSourceProperty =
            DependencyProperty.Register("ItemSource", typeof(IList<ArcElement>), typeof(CircleBread), new PropertyMetadata(new List<ArcElement> { 
                new ArcElement("AAA",15.0),
                new ArcElement("A2A",35.0),
                new ArcElement("A3A",5.0),
                new ArcElement("A1A",25.0),
            },(ss,ee)=> {
                var uc = ss as CircleBread;
                uc.UpdateLayout();
            }));

        public static void WidthHeightPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var uc = d as CircleBread;
            if ((double)e.NewValue < uc.Radius * 2.0)
            {
                uc.SetValue(e.Property, (double)e.OldValue);
                throw new ArgumentOutOfRangeException($"{e.Property.Name}必须大于Radius属性的两倍");
            }
            uc?.UpdateLayout();

        }
        private void RegisteInternalEvent()
        {
            UserControl.WidthProperty.OverrideMetadata(typeof(CircleBread), new FrameworkPropertyMetadata(new PropertyChangedCallback(WidthHeightPropertyChangedCallback)));
            UserControl.HeightProperty.OverrideMetadata(typeof(CircleBread), new FrameworkPropertyMetadata(new PropertyChangedCallback(WidthHeightPropertyChangedCallback)));
        }
        private void OnLoaded(object sender, RoutedEventArgs e)
        {           
            UpdateLayout();
        }
        private Point GetPointByAngle(double angle)
        {          
            double startX, startY;
            if (angle <= 90)
            {
                startX = Math.Sin(Math.PI/180.0 * angle) * Radius + Center.X;
                startY = Center.Y - Math.Cos(Math.PI / 180.0 * angle) * Radius;
            }
            else if (angle <= 180)
            {
                startX = Math.Cos(Math.PI / 180.0 * (angle - 90.0)) * Radius + Center.X;
                startY = Math.Sin(Math.PI / 180.0 * (angle - 90.0)) * Radius + Center.Y;
            }
            else if (angle <= 270)
            {
                startX = Center.X - Math.Sin(Math.PI / 180.0 * (angle - 180.0)) * Radius;
                startY = Math.Cos(Math.PI / 180.0 * (angle - 180.0)) * Radius + Center.Y;
            }
            else
            {
                startX = Center.X - Math.Cos(Math.PI / 180.0 * (angle - 270.0)) * Radius;
                startY = Center.Y - Math.Sin(Math.PI / 180.0 * (angle - 270.0)) * Radius;
            }
            return new Point(startX, startY);
        }
        private void OnClickPath(object sender, MouseButtonEventArgs e) 
        {
            PathClickEvent?.Invoke(sender as System.Windows.Shapes.Path);
        }

        public new void UpdateLayout()
        {
            Center = new Point(this.Width / 2.0, this.Height / 2.0);
            main.Children.Clear();
            Point startPoint = GetPointByAngle(StartAngle);
            if (ItemSource == null || ItemSource.Count == 0)
                return;
            else
            {
                double usedAngle = 0.0;
                Random rd = new Random(DateTime.Now.Millisecond);
                Stack<byte> st = new Stack<byte>();
                for(int i = 0; i < ItemSource.Count * 3; i++)
                {
                    st.Push(Convert.ToByte(rd.Next(250)));
                }
               
                foreach (var item in ItemSource)
                {
                    System.Windows.Shapes.Path ph = new System.Windows.Shapes.Path();
                    ph.MouseDown += OnClickPath;
                    ph.Stroke = new SolidColorBrush(Color.FromRgb(st.Pop(), st.Pop(), st.Pop()));
                    ph.StrokeThickness = StrokeTrickness;
                    double rotateAngel = item.Percentage / 100.0 * 360.0;
                    usedAngle += rotateAngel;
                    //弧
                    Point StartPoint = GetPointByAngle(usedAngle - rotateAngel);
                    Point pt = GetPointByAngle(usedAngle);
                    bool isLarge = rotateAngel > 180 ? true : false;
                    ArcSegment seg = new ArcSegment(pt, new Size(Radius, Radius), 0, isLarge, SweepDirection.Clockwise, true);   
                    var figure = new PathFigure(StartPoint, new List<PathSegment>{
                            seg
                        }, false);                                       
                    Point labelLineEnd = new Point();
                    Point labelLineStart = GetPointByAngle(usedAngle - rotateAngel / 2.0);
                    
                    Rect rect = new Rect();
                    if (usedAngle <= 90)
                    {
                        labelLineEnd.X = labelLineStart.X + 60;
                        labelLineEnd.Y = labelLineStart.Y - 10;
                        rect.Location = new Point(labelLineEnd.X, labelLineEnd.Y);
                    }
                    else if(usedAngle <= 180)
                    {
                        labelLineEnd.X = labelLineStart.X + 60;
                        labelLineEnd.Y = labelLineStart.Y + 10;
                        rect.Location = new Point(labelLineEnd.X, labelLineEnd.Y);
                    }
                    else if(usedAngle <= 270)
                    {
                        labelLineEnd.X = labelLineStart.X - 60;
                        labelLineEnd.Y = labelLineStart.Y + 10;
                        rect.Location = new Point(labelLineEnd.X, labelLineEnd.Y);
                    }
                    else
                    {
                        labelLineEnd.X = labelLineStart.X - 60;
                        labelLineEnd.Y = labelLineStart.Y - 10;
                        rect.Location = new Point(labelLineEnd.X, labelLineEnd.Y);
                    }
                    PathGeometry pg = new PathGeometry(new List<PathFigure> {
                        figure
                    });
                    ph.Data = pg;
                    main.Children.Add(ph);
                    //....end 
                    //标签文本
                    System.Windows.Shapes.Path ph1 = new System.Windows.Shapes.Path();
                    ph1.Stroke = ph.Stroke;
                    ph1.StrokeThickness = 2;
                    FormattedText text = new FormattedText(item.Title, new System.Globalization.CultureInfo("zh-cn"), FlowDirection.LeftToRight, new Typeface(new FontFamily("楷体"), FontStyles.Italic, FontWeights.Normal, FontStretches.Normal), 14, Brushes.Black);
                    Geometry geo = text.BuildGeometry(new Point(labelLineStart.X > this.Width/2.0 ? (this.Width - 15.0 - text.Width) : 15.0, labelLineStart.Y));
                    ph1.Data = geo;
                    main.Children.Add(ph1);
                    //end
                    //标签连线
                    System.Windows.Shapes.Path ph0 = new System.Windows.Shapes.Path();
                    ph0.Stroke = ph.Stroke;
                    ph0.StrokeThickness = 2;
                    ph0.Data = new LineGeometry(labelLineStart, new Point(geo.Bounds.Right,labelLineStart.Y));
                    main.Children.Add(ph0);
                    //....end                                       

                }
            }
        }
        public class ArcElement
        {
            public ArcElement(string title,double percentage)
            {
                Title = title;
                Percentage = percentage;
            }
            /// <summary>
            /// 数据百分比
            /// </summary>
            public double Percentage { get; set; }
            public string Title { get; set; }
        }
    }
}
